home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / dos / proc.1 < prev    next >
Text File  |  1989-03-15  |  20KB  |  486 lines

  1. Path: xanth!ukma!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i065:  proc - createproc() w/o loadseg()
  5. Message-ID: <12257@swan.ulowell.edu>
  6. Date: 15 Mar 89 21:55:02 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 475
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: well!ewhac (Leo 'Bols Ewhac' Schwab)
  12. Posting-number: Volume 89, Issue 65
  13. Archive-name: dos/proc.1
  14.  
  15. #    This is a shell archive.
  16. #    Remove everything above and including the cut line.
  17. #    Then run the rest of the file through sh.
  18. #----cut here-----cut here-----cut here-----cut here----#
  19. #!/bin/sh
  20. # shar:    Shell Archiver
  21. #    Run the following text with /bin/sh to create:
  22. #    README
  23. #    proc.c
  24. #    proc.uu
  25. # This archive created: Wed Mar 15 16:53:01 1989
  26. cat << \SHAR_EOF > README
  27.     This is a little goodie I cooked up as a simple diversion before I
  28. started back into working on "Onion".  This is written in Manx C 3.4b.
  29. Translation to Lattice should be straightforward.
  30.  
  31. MANUFACTURE:
  32. 1> cc proc.c
  33. 1> ln proc.o -lc
  34.  
  35.     That's all there is to it.  The source code has copious comments,
  36. which I hope illustrate what's going on.  Perhaps some CATS people would
  37. care to comment on just how supportable this trick is.
  38.  
  39.     I hope you like it.
  40.  
  41. _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  42. Leo L. Schwab -- The Guy in The Cape    INET: well!ewhac@ucbvax.Berkeley.EDU
  43.  \_ -_        Recumbent Bikes:    UUCP: pacbell > !{well,unicom}!ewhac
  44. O----^o          The Only Way To Fly.          hplabs / (pronounced "AE-wack")
  45. "Work FOR?  I don't work FOR anybody!  I'm just having fun."  -- The Doctor
  46. SHAR_EOF
  47. cat << \SHAR_EOF > proc.c
  48. /*  :ts=8 bk=0
  49.  *
  50.  * proc.c:    Illustration of how to create a full-fledged DOS process
  51.  *        without needing to LoadSeg() it first.  Based on an idea
  52.  *        presented at BADGE (don't remember the name of the guy
  53.  *        who thought it up).
  54.  *
  55.  * Leo L. Schwab                8903.01
  56.  */
  57. #include <exec/types.h>
  58. #include <libraries/dosextens.h>
  59.  
  60. extern void    *AllocMem(), *CreatePort(), *GetMsg(), *FindTask();
  61.  
  62. extern LONG    Open(), Output(), CreateProc();
  63.  
  64.  
  65. /*
  66.  * Cursor manipulation strings.
  67.  */
  68. char    fwdcursor[] = "\033[C";
  69. char    backcursor = '\b';
  70.  
  71. /*
  72.  * Buried deep in the AmigaDOS Technical Reference Manual, the structure of
  73.  * of memory lists and SegLists are described.  A SegList consists of a BPTR
  74.  * to the next element in the list (NULL-terminated), followed by code.
  75.  * The pointers point to the NextSeg field.  The size of the node in bytes
  76.  * is stored in the longword just before NextSeg field.  However, the
  77.  * size is only of real interest to UnLoadSeg(), which you won't be calling
  78.  * in this case.  So we don't need it.
  79.  *
  80.  * So the PhonySegList structure consists merely of a NextSeg field, which
  81.  * is initialized to NULL (CreateProc() ignores this field, anyway), followed
  82.  * by code to execute, which CreateProc() will jump to.  The code we give it
  83.  * to execute is an absolute jump to the real entry point, which you
  84.  * initialize appropriately.
  85.  */
  86. struct PhonySegList {
  87.     BPTR    psl_NextSeg;        /*  BPTR to next element in list  */
  88.     UWORD    psl_JMP;        /*  A 68000 JMP abs.l instruction  */
  89.     LONG    (*psl_EntryPoint)();    /*  The address of the function  */
  90. };
  91.  
  92. /*
  93.  * This is NOT the structure that will actually get passed to CreateProc().
  94.  * AmigaDOS demands that everything, including SegLists, be on longword
  95.  * boundaries.  Short of compiler-dependent switches, the only way to
  96.  * guarantee correct alignment is to AllocMem() a PhonySegList structure,
  97.  * and copy this template into it.  You should also remember to initialize
  98.  * the psl_EntryPoint field (the argument to the JMP instruction) in the
  99.  * allocated structure, for obvious reasons.
  100.  */
  101. struct PhonySegList template = {
  102.     NULL,                /*  No next element.          */
  103.     0x4EF9,                /*  JMP abs.l              */
  104.     NULL                /*  Argument for JMP instruction  */
  105. };
  106.  
  107.  
  108. /*
  109.  * This is the routine that will be CreateProc()ed.  Due to the global nature
  110.  * of library base variables, any library routines this routine calls will
  111.  * be using library base variables that, strictly speaking, belong to the
  112.  * "parent" process.  Despite the fact that it will work, this is
  113.  * nevertheless a dangerous practice.  In an ideal world, you would want to
  114.  * convince the linker to resolve all library base pointer references to 
  115.  * your co-process's own copies.  Lattice, on the other hand, due to its
  116.  * #pragmas, may not have this problem (can someone confirm this?).
  117.  */
  118. LONG
  119. coprocess ()
  120. {
  121.     register int    i, n;
  122.     struct Process    *me;
  123.     struct Message    *startupmsg;
  124.     LONG        doswin;
  125.     char        buf[256];
  126.  
  127. #ifdef    AZTEC_C
  128. #ifndef    _LARGE_DATA
  129.     /*
  130.      * This gets to be a bloody nuisance.
  131.      */
  132.     geta4 ();
  133. #endif
  134. #endif
  135.     /*
  136.      * Startup messages are important.  Not only can they provide
  137.      * configuration information for the newly-created process (what
  138.      * directory you're in, what size to make your window, etc.), but
  139.      * they also serve to inform the program that started you that
  140.      * you have finished executing.  This is important, because the
  141.      * parent program will want to know when it's okay to free the
  142.      * resources it allocated to start you.  In this example, we
  143.      * grab the message so that we can reply it.  The act of replying
  144.      * the startup message will inform the parent that we're done.
  145.      */
  146.     me = FindTask (NULL);
  147.     WaitPort (&me -> pr_MsgPort);
  148.     startupmsg = GetMsg (&me -> pr_MsgPort);
  149.  
  150.     /*
  151.      * Recall that, because a global DOSBase pointer has been initialized
  152.      * by the parent, and because the stubs will reference it at the link
  153.      * stage, we don't need to open dos.library here.
  154.      */
  155.     if (!(doswin = Open ("CON:0/0/320/100/Sub-Process", MODE_NEWFILE)))
  156.         goto xit;
  157.  
  158.     /*
  159.      * Say hello.
  160.      */
  161.     Write (doswin, "This is the child speaking.\n", 28L);
  162.  
  163.     /*
  164.      * Print the value of FindTask(NULL) to prove we're a separate
  165.      * task, and print some values in the Process structure to prove
  166.      * we're a real process.
  167.      *
  168.      * (Another caveat:  The stdio functions in this example (like
  169.      * sprintf()) are being shared by both processes.  Some stdio
  170.      * routines utilize a set of global variables, access to which is
  171.      * not arbitrated.  Therefore, it is possible for the processes to
  172.      * collide when calling stdio functions, causing Bad Things to
  173.      * happen.  Be aware of this when doing your own multiprogramming.
  174.      * (I'm pretty sure sprintf() is safe.))
  175.      */
  176.     sprintf (buf, "I'm at 0x%lx\n", me);
  177.     Write (doswin, buf, (LONG) strlen (buf));
  178.  
  179.     sprintf (buf, "My stack size appears to be\n%ld bytes.\n",
  180.          me -> pr_StackSize);
  181.     Write (doswin, buf, (LONG) strlen (buf));
  182.  
  183.     sprintf (buf, "pr_StackBase is 0x%lx\n", me -> pr_StackBase);
  184.     Write (doswin, buf, (LONG) strlen (buf));
  185.  
  186.     /*
  187.      * Make the cursor in the window zot back and forth, which will
  188.      * happen concurrently with the same action in the CLI window,
  189.      * proving beyond a shadow of a doubt that there really are two
  190.      * separate programs running.
  191.      */
  192.     for (n = 20;  n--; ) {
  193.         for (i = 35;  i--; )
  194.             Write (doswin, fwdcursor, sizeof (fwdcursor) - 1L);
  195.         for (i = 35;  i--; )
  196.             Write (doswin, &backcursor, 1L);
  197.     }
  198.  
  199.     /*
  200.      * We've proved our existence.  We now reply our startup message,
  201.      * and exit.  Note the use of Forbid() without a corresponding
  202.      * Permit().  This prevents this process from being switched out
  203.      * before exiting completely.  When this process is totally gone,
  204.      * Exec will notice, and do the equivalent of a Permit() internally.
  205.      */
  206.     Close (doswin);            /*  Get ridda da window.  */
  207. xit:
  208.     Forbid ();
  209.     ReplyMsg (startupmsg);
  210.     return (0);
  211. }
  212.  
  213.  
  214. /*
  215.  * Here is the main program.  Its job will be to create the support
  216.  * structures to fire off the sub-process, print out some relevant
  217.  * information, then while away the time until the child exits.
  218.  */
  219. main ()
  220. {
  221.     register int        i;
  222.     struct Process        *me;
  223.     struct MsgPort        *replyport = NULL;
  224.     struct Message        startupmsg;
  225.     struct PhonySegList    *fakelist = NULL;
  226.     LONG            child,
  227.                 priority,
  228.                 term;
  229.  
  230.     /*
  231.      * Get a handle on the current output stream so that we can Write()
  232.      * to it.  Note that this implies this program really ought to be
  233.      * run from a CLI.
  234.      */
  235.     if (!(term = Output ()))
  236.         goto xit;
  237.  
  238.     /*
  239.      * Create a message port for the startup message to be replied to.
  240.      */
  241.     if (!(replyport = CreatePort (NULL, NULL)))
  242.         goto xit;
  243.  
  244.     /*
  245.      * Allocate a PhonySegList structure.
  246.      */
  247.     if (!(fakelist = AllocMem ((LONG) sizeof (*fakelist), NULL)))
  248.         goto xit;
  249.  
  250.     /*
  251.      * Copy the template into the allocated memory, and set the entry
  252.      * point to the sub-process.
  253.      */
  254.     CopyMem (&template, fakelist, (LONG) sizeof (template));
  255.     fakelist -> psl_EntryPoint = coprocess;
  256.  
  257.     /*
  258.      * Initialize the startup message.  There's nothing really amazing
  259.      * happening here.  Its sole purpose in life is to be replied.
  260.      */
  261.     startupmsg.mn_Node.ln_Type    = NT_MESSAGE;
  262.     startupmsg.mn_Node.ln_Pri    = 0;
  263.     startupmsg.mn_ReplyPort        = replyport;
  264.  
  265.     /*
  266.      * Find ourselves.  Discover what priority we're running at, so that
  267.      * we can start the sub-process at the same priority.
  268.      */
  269.     me = FindTask (NULL);
  270.     priority = me -> pr_Task.tc_Node.ln_Pri;
  271.  
  272.     /*
  273.      * Print some information about ourselves.
  274.      */
  275.     puts ("This is the parent speaking.");
  276.     printf ("I'm at 0x%lx\n", me);
  277.     printf ("My stack size appears to be\n%ld bytes.\n",
  278.          me -> pr_StackSize);
  279.     printf ("pr_StackBase is 0x%lx\n", me -> pr_StackBase);
  280.  
  281.     /*
  282.      * Now, create and start the sub-process.  The current release of
  283.      * AmigaDOS CreateProc() does nothing special with SegLists.
  284.      * All it uses it for is to find the first bit of code to execute.
  285.      * By passing it 'fakelist', we're essentially feeding it a JMP
  286.      * instruction which jumps to the real start of our sub-process.
  287.      * (Note that we have to convert 'fakelist' into a BPTR.)
  288.      * Thus, we get a full-fledged DOS process, which we can do things
  289.      * with, and we didn't have to LoadSeg() it.
  290.      */
  291.     if (!(child = CreateProc ("Sub-Process",
  292.                   priority,
  293.                   (LONG) fakelist >> 2,
  294.                   2048L)))
  295.         goto xit;
  296.  
  297.     /* 
  298.      * Send the startup message.  This will get the sub-process doing
  299.      * its thing.
  300.      * (Note that CreateProc() returns a pointer, not to the process
  301.      * structure, but to the pr_MsgPort structure *within* the process
  302.      * structure.)
  303.      */
  304.     PutMsg (child, &startupmsg);
  305.  
  306.     /*
  307.      * Make our cursor fly back and forth, which hopefully will happen
  308.      * concurrently with the sub-process's activity.  We continue to
  309.      * do this until the sub-process replies its message, making
  310.      * GetMsg() return a non-NULL value.  Since we "know" what's arriving
  311.      * at the replyport, we can safely throw the result from GetMsg()
  312.      * away.
  313.      */
  314.     while (!GetMsg (replyport)) {
  315.         for (i = 64;  i--; )
  316.             Write (term, fwdcursor, sizeof (fwdcursor) - 1L);
  317.         for (i = 64;  i--; )
  318.             Write (term, &backcursor, 1L);
  319.     }
  320.  
  321.     /*
  322.      * At this point, the sub-process has completely exited.  We may
  323.      * now safely deallocate all the support structures, and exit.
  324.      */
  325.     puts ("Child terminated.");
  326. xit:
  327.     if (fakelist)    FreeMem (fakelist, (LONG) sizeof (*fakelist));
  328.     if (replyport)    DeletePort (replyport);
  329. }
  330. SHAR_EOF
  331. cat << \SHAR_EOF > proc.uu
  332.  
  333. begin 644 proc
  334. M```#\P`````````#``````````(```61````J@````$```/I```%D4[Z`_I.1
  335. M5?[T2.<,`$ZZ!&!"ITZZ%5183RM`__P@;?_\2&@`7$ZZ%>A83R!M__Q(:`!<I
  336. M3KH5<%A/*T#_^$AX`^Y(>@$P3KH3O%!/*T#_]&<``0I(>``<2'H!-B\M__1.R
  337. MNA/`3^\`#"\M__Q(>@$_2&W^]$ZZ"#9/[P`,2&W^]$ZZ#.)83TC`+P!(;?[T)
  338. M+RW_]$ZZ$XY/[P`,(&W__"\H`(1(>@$72&W^]$ZZ"`!/[P`,2&W^]$ZZ#*Q8_
  339. M3TC`+P!(;?[T+RW_]$ZZ$UA/[P`,(&W__"\H`)!(>@$)2&W^]$ZZ!\I/[P`,^
  340. M2&W^]$ZZ#'983TC`+P!(;?[T+RW_]$ZZ$R)/[P`,>A1@0'@C8!1(>``#2&R``
  341. M`B\M__1.NA,&3^\`##`$4T1*0&;D>"-@%$AX``%(;(`&+RW_]$ZZ$N9/[P`,^
  342. M,`131$I`9N0P!5-%2D!FN"\M__1.NA)46$].NA0<+RW_^$ZZ%(Q83W``3-\`M
  343. M,$Y=3G5#3TXZ,"\P+S,R,"\Q,#`O4W5B+5!R;V-E<W,`5&AI<R!I<R!T:&4@%
  344. M8VAI;&0@<W!E86MI;F<N"@!))VT@870@,'@E;'@*`$UY('-T86-K('-I>F4@2
  345. M87!P96%R<R!T;R!B90HE;&0@8GET97,N"@!P<E]3=&%C:T)A<V4@:7,@,'@EO
  346. M;'@*`$Y5_]0O!$*M__A"K?_@3KH2#"M`_]1G``$Z0J="ITZZ$E103RM`__AGY
  347. M``$H0J=(>``*3KH3*E!/*T#_X&<``11(>``*+RW_X$AL@`A.NA(43^\`#$'ZZ
  348. M_;8B;?_@(T@`!AM\``7_[$(M_^TK;?_X__)"ITZZ$OQ83RM`__P@;?_\$"@``
  349. M"4B`2,`K0/_82'H`[DZZ!3)83R\M__Q(>@#]3KH&7E!/(&W__"\H`(1(>@#YP
  350. M3KH&3%!/(&W__"\H`)!(>@$/3KH&.E!/2'@(`"`M_^#D@"\`+RW_V$AZ`0Q.X
  351. MNA#D3^\`$"M`_]QG:$AM_^0O+?_<3KH2ZE!/+RW_^$ZZ$JY83TJ`9D)X0&`4C
  352. M2'@``TAL@`(O+?_43KH1$$_O``PP!%-$2D!FY'A`8!1(>``!2&R`!B\M_]1.>
  353. MNA#P3^\`##`$4T1*0&;D8+!(>@"H3KH$=EA/2JW_X&<.2'@`"B\M_^!.NA(J:
  354. M4$]*K?_X9PHO+?_X3KH1CEA/*!].74YU5&AI<R!I<R!T:&4@<&%R96YT('-PV
  355. M96%K:6YG+@!))VT@870@,'@E;'@*`$UY('-T86-K('-I>F4@87!P96%R<R!T!
  356. M;R!B90HE;&0@8GET97,N"@!P<E]3=&%C:T)A<V4@:7,@,'@E;'@*`%-U8BU0$
  357. M<F]C97-S`$-H:6QD('1E<FUI;F%T960N`&%P0^R"8D7L@F*UR68.,CP`$6L(+
  358. M=``BPE')__PI3X)J+'@`!"E.@FY(YX"`""X`!`$I9Q!+^@`(3J[_XF`&0J?S6
  359. M7TYS0_H`($ZN_F@I0()R9@PN/``#@`=.KO^48`1.N@`:4$].=61O<RYL:6)RV
  360. M87)Y`$GY``!__DYU3E4``"\*2'D``0``,"R"7L'\``8O`$ZZ$,PI0()V4$]FD
  361. M%$*G2'D``0``3KH/D%!/+FR":DYU(&R"=D)H``0@;()V,7P``0`0(&R"=C%\S
  362. M``$`"B!L@FH@+()JD*@`!%"`*4"">B!L@GH@O$U!3EA"ITZZ$(0D0$JJ`*Q8E
  363. M3V<N+RT`#"\M``@O"DZZ`*XY?``!@GX@;()V`&B````$(&R"=@!H@```"D_OY
  364. M``Q@0DAJ`%Q.NA#J2&H`7$ZZ$'@I0(*`(&R"@$JH`"103V<0(&R"@")H`"0OL
  365. M$4ZZ#H!83R\L@H`O"DZZ`IPI;(*`@H103TZZ#H`@;()V((!.N@ZH(&R"=B%`C
  366. M``9G%DAX`^U(>@`J3KH.@"!L@G8A0``,4$\O+(*$/RR"B$ZZ_%Q"9TZZ#+!0X
  367. M3R1?3EU.=2H`3E4``$CG##`D;0`0(&T`""`H`*SE@"@`($0@*``0Y8`F0!`39
  368. M2(!(P-"M``Q4@#E`@HI"IS`L@HI(P"\`3KH/;BE`@HQ03V8(3-\,,$Y=3G40H
  369. M$TB`.@`_!2!+4H@O""\L@HQ.N@%.2'H!2#`%2,#0K(*,+P!.N@&(/RT`#B\*:
  370. M+RR"C$ZZ`58@;(*,0C!0`#E\``&"B#`%2,#0K(*,)D!2BR1+3^\`'!`32(`Z<
  371. M`+!\`"!G&+I\``EG$KI\``QG#+I\``UG!KI\``IF!%*+8-@,$P`@;7H,$P`B.
  372. M9BY2BR!+4HL0$$B`.@!G'B!*4HH0A;I\`")F$`P3`")F!%*+8`9"*O__8`)@C
  373. MUF`X($M2BQ`02(`Z`&<FNGP`(&<@NGP`"6<:NGP`#&<4NGP`#6<.NGP`"F<(F
  374. M($I2BA"%8,X@2E**0A!*168"4XM2;(*(8`#_6D(20J<P+(*(4D!(P.6`+P!.6
  375. MN@Y,*4""A%!/9@A";(*(8`#^V'H`)FR"C&`>,`5(P.6`(&R"A"&+"``O"TZZ`
  376. M!BA20$C`U\!83U)%NFR"B&W<,`5(P.6`(&R"A$*P"`!@`/Z:(`!,[P,```0@^
  377. M"#(O``Q@`A#95\G__&<&4D%@`D(84<G__$YU,#Q__V`$,"\`#"!O``1*&&;\_
  378. M4T@B;P`(4T`0V5?(__QG`D(0("\`!$YU(&\`!"`((F\`"!#99OQ.=4Y5```O/
  379. M"B1M``A*$F<@($I2BA`02(`_`$ZZ!9ZP?/__5$]F"'#_)%].74YU8-P_/``*Z
  380. M3KH%A%1/8.Q.50``2.<.,"1M``A"ITAZ`(Y.N@V\*4""D%!/9@A,WPQP3EU.;
  381. M=2!M``PB:``D+RD`!$ZZ#@XH`%A/9U)(>@!M($0O*``V3KH-X"9`2H!03V<T#
  382. M2'@#[2\+3KH+MBP`4$]G)"`&Y8`J`"!%)6@`"`"D)48`G$AX`^U(>@`X3KH+@
  383. MDB5``*!03R\$3KH-K%A/+RR"D$ZZ"\!"K(*06$]@@&EC;VXN;&EB<F%R>0!7[
  384. M24Y$3U<`*@!.50``+P0I;0`(@F)(;0`0+RT`#$AZ`!I.N@#<.``@;()B0A`P:
  385. M!$_O``PH'TY=3G5.50``(&R"8E*L@F(0+0`)$(!(@,!\`/].74YU3E4``$AM7
  386. M``PO+0`(2'H$<$ZZ`)A/[P`,3EU.=4Y5``!(YP@@)&T`#@QM``0`$F8((&T`A
  387. M""@08!Q*;0`,;PP@;0`(<``P$"@`8`H@;0`(,!!(P"@`0FT`$DIM``QL$$1M'
  388. M``Q*A&P(1(0[?``!`!(R+0`,2,$@!$ZZ`XY![(`24XH4L```,BT`#$C!(`1.7
  389. MN@.$*`!FVDIM`!)G!E.*%+P`+2`*3-\$$$Y=3G5.5?\B2.<(,"1M``@F;0`,S
  390. M0FW_^BMM`!#__"!+4HL0$$B`.`!G``+LN'P`)68``LI"+?\P.WP``?_X.WP`7
  391. M(/_V.WPG$/_T($M2BQ`02(`X`+!\`"UF#D)M__@@2U*+$!!(@#@`N'P`,&803
  392. M.WP`,/_V($M2BQ`02(`X`+A\`"IF&"!M__Q4K?_\.U#_\B!+4HL0$$B`.`!@H
  393. M,D)M__)@'#`M__+!_``*T$20?``P.T#_\B!+4HL0$$B`.``P!%)`0>R`)`@P6
  394. M``(``&;4N'P`+F9:($M2BQ`02(`X`+!\`"IF&"!M__Q4K?_\.U#_]"!+4HL0T
  395. M$$B`.`!@,D)M__1@'#`M__3!_``*T$20?``P.T#_]"!+4HL0$$B`.``P!%)`#
  396. M0>R`)`@P``(``&;4.WP``O_PN'P`;&82($M2BQ`02(`X`#M\``3_\&`0N'P`[
  397. M:&8*($M2BQ`02(`X`#`$2,!@>#M\``C_[F`6.WP`"O_N8`X[?``0_^Y@!CM\C
  398. M__;_[C\M__!(;?\P/RW_[B\M__Q.NOWD*T#_ZC`M__!(P-&M__Q/[P`,8%H@_
  399. M;?_\6*W__"M0_^HO+?_J3KH"##M`__!83V!*(&W__%2M__PX$$'M_R\K2/_JF
  400. M$(1@*)"\````8V?B4X!GE)"\````"V<`_W19@&>T58!G`/]R5X!G`/]T8,Q!2
  401. M[?\PD>W_ZCM(__`P+?_PL&W_]&\&.VW_]/_P2FW_^&=H(&W_Z@P0`"UG"B!M*
  402. M_^H,$``K9BX,;0`P__9F)E-M__(@;?_J4JW_ZA`02(`_`$Z2L'S__U1/9@IPQ
  403. M_TS?#!!.74YU8!8_+?_V3I*P?/__5$]F!'#_8.12;?_Z,"W_\E-M__*P;?_P8
  404. M;MQ";?_N8"`@;?_J4JW_ZA`02(`_`$Z2L'S__U1/9@1P_V"P4FW_[B!M_^I*M
  405. M$&<*,"W_[K!M__1MSC`M_^[1;?_Z2FW_^&8H8!@_/``@3I*P?/__5$]F!G#_N
  406. M8`#_>%)M__HP+?_R4VW_\K!M__!NVF`6/P1.DK!\__]43V8&</]@`/]24FW_2
  407. M^F``_0HP+?_Z8`#_0DCG2`!"A$J`:@1$@%)$2H%J!D2!"D0``6$^2D1G`D2`0
  408. M3-\`$DJ`3G5(YT@`0H1*@&H$1(!21$J!:@)$@6$:(`%@V"\!81(@`2(?2H!.&
  409. M=2\!808B'TJ`3G5(YS``2$%*068@2$$V`30`0D!(0(##(@!(0#("@L,P`4)!K
  410. M2$%,WP`,3G5(028!(@!"04A!2$!"0'0/T(#3@;:!8@22@U)`4<K_\DS?``Q.G
  411. M=2!O``0@"$H89OR1P"`(4X!.=4Y5``!(;("\/RT`"$ZZ``A<3TY=3G5.50``6
  412. M+P0X+0`(+RT`"C\$3KH`,+A\``I<3V8D(&T`"A`H``Q(@`@```=G%#\\__\O)
  413. M+0`*3KH`]%Q/*!].74YU8/A.50``+PHD;0`*(%*QZ@`$91@P+0`(P'P`_S\`/
  414. M+PI.N@#(7$\D7TY=3G4@4E*2$"T`"1"`2(#`?`#_8.A.50``+PI![("F)$@@7
  415. M2M7\````%B\(81!83T'L@EZUR&7J)%].74YU3E4``$CG""`D;0`(>``@"F8*O
  416. M</],WP003EU.=4HJ``QG4`@J``(`#&<,/SS__R\*85(X`%Q/$"H`#4B`/P!.J
  417. MN@3NB$`(*@`!``Q43V<*+RH`"$ZZ`BY83P@J``4`#&<2+RH`$DZZ`L`O*@`2C
  418. M3KH"%%!/0I)"J@`$0JH`"$(J``PP!&"03E7__DCG""`D;0`(0?K_1BE(@I0(P
  419. M*@`$``QG"G#_3-\$$$Y=3G4(*@`"``QG,"`2D*H`"#@`/P0O*@`($"H`#4B`]
  420. M/P!.N@*`L$103V<0".H`!``,0I)"J@`$</]@P`QM__\`#&80"*H``@`,0I)"W
  421. MJ@`$<`!@J$JJ``AF""\*3KH`FEA/#&H``0`09BH;;0`-__\_/``!2&W__Q`JE
  422. M``U(@#\`3KH"(K!\``%03V:@,"T`#&``_VHDJ@`(,"H`$$C`T*H`""5```0(_
  423. MZ@`"``P@4E*2$"T`#1"`2(#`?`#_8`#_/DY5```O"D'L@*8D2$HJ``QG&-7\S
  424. M````%D'L@EZUR&4(<``D7TY=3G5@XD*20JH`!$*J``@@"F#J3E7__"\*)&T`$
  425. M"#\\!`!.N@#`*T#__%1/9A@U?``!`!`@2M'\````#B5(``@D7TY=3G4U?`0`X
  426. M`!`(Z@`!``PE;?_\``@0*@`-2(`_`$ZZ`.)*0%1/9P8`*@"```Q@SDY5``!(%
  427. MYP`P)&R"9F`4)E(@*@`$4(`O`"\*3KH%0%!/)$L@"F;H0JR"9DS?#`!.74YU/
  428. M3E4``"\*0?K_QBE(@IA"IR`M``A0@"\`3KH$XB1`2H!03V8(<``D7TY=3G4D2
  429. MK()F)6T`"``$*4J"9B`*4(!@YDY5``!P`#`M``@O`&&R6$].74YU3E4``$CGN
  430. M`#"7RR1L@F9@#B!M``A1B+'*9Q(F2B12(`IF[G#_3-\,`$Y=3G4@"V<$)I)@`
  431. M!"E2@F8@*@`$4(`O`"\*3KH$DG``4$]@V$Y5```O"C`M``C!_``&)$#5[()VS
  432. M2FT`"&T.,"T`"+!L@EYL!$J29@XY?``"@IQP_R1?3EU.=3`M``C!_``&(&R"&
  433. M=B\P"`!.N@*P2H!83V<$<`%@`G``8-A.50``+RT`"$ZZ`GI*@%A/9@Y.N@*$L
  434. M.4""G'#_3EU.=7``8/A.50``2.<,(#@M``A.N@!P,`3!_``&)$#5[()V2D1MN
  435. M"KAL@EYL!$J29A`Y?``"@IQP_TS?!#!.74YU,"H`!,!\``-F"CE\``6"G'#_U
  436. M8.1P`#`M``XO`"\M``HO$DZZ`DPJ`+"\_____T_O``QF#$ZZ`@0Y0(*<</]@-
  437. MN"`%8+1.5?_\2'@0`$*G3KH#_"M`__P(```,4$]G$DIL@GYF""`M__Q.74YU[
  438. M3KH`!G``8/1.50``2'@`!$AZ`!Q.N@'B+P!.N@'H/SP``4ZZ``Y/[P`.3EU.Y
  439. M=5Y#"@!.50``2JR"E&<&(&R"E$Z0/RT`"$ZZ``A43TY=3G5.5?_\+P0P+0`('
  440. M2,`K0/_\2JR"=F<H>`!@"C\$3KH`T%1/4D2X;()>;?`P+()>P?P`!B\`+RR"-
  441. M=DZZ`MY03TJL@IAG!B!L@IA.D$JL@IYG"B\L@IY.N@&(6$]*K(*B9PHO+(*B"
  442. M3KH!>%A/2JR"IF<*+RR"IDZZ`6A83RQX``0(+@`$`2EG%"\-2_H`"DZN_^(JK
  443. M7V`&0J?S7TYS2JR"@&8P2JR"C&<H,"R"BDC`+P`O+(*,3KH"9#`L@HA20$C`8
  444. MY8`O`"\L@H1.N@)03^\`$&`.3KH".B\L@H!.N@*J6$\@+?_\+FR":DYU*!].M
  445. M74YU3E4``$CG#B`X+0`(,`3!_``&)$#5[()V2D1M"KAL@EYL!$J29A`Y?``"C
  446. M@IQP_TS?!'!.74YU""H`!P`$9@@O$DZZ``Y83T*2<`!@XD[Z``(B+P`$+&R""
  447. M<D[N_]PO!$SO`!X`""QL@G).KO]V*!].=2(O``0L;()R3N[_@B(O``0L;()RC
  448. M3N[_N"QL@G).[O_*+&R"<D[N_WPB+P`$+&R"<D[N_RA.^@`"3.\`!@`$+&R"T
  449. M<D[N_^).^@`"+&R"<D[N_\1.^@`"3.\`#@`$+&R"<D[N_]!(YP$$3.\@@``,M
  450. M+&R";DZN_Y1,WR"`3G5.^@`"(F\`!"QL@FY.[OYB3.\#```$("\`#"QL@FY.;
  451. M[OV03E4``$CG""!(>/__3KH`T"@`L+S_____6$]F"G``3-\$$$Y=3G5(>0`!1
  452. M``%(>``B3KH`N"1`2H!03V8,+P1.N@#L<`!83V#6)6T`"``*%6T`#P`)%7P`E
  453. M!``(0BH`#A5$``]"ITZZ`)8E0``02JT`"%A/9PHO"DZZ`%I83V`*2&H`%$ZZ[
  454. M`,183R`*8)).50``+PHD;0`(2JH`"F<(+PI.N@#86$\5?`#_``@E?/____\`>
  455. M%'``$"H`#R\`3KH`<$AX`"(O"DZZ`%)/[P`,)%].74YU(F\`!"QL@FY.[OZ>9
  456. M("\`!"QL@FY.[OZV3OH``DSO``,`!"QL@FY.[O\Z3OH``B)O``0L;()N3N[^U
  457. MVD[Z``(L;()N3N[_?$[Z``(B;P`$("\`""QL@FY.[O\N("\`!"QL@FY.[OZP/
  458. M3OH``B!O``0L;()N3N[^C"!O``0@B%B00J@`!"%(``A.=2QL@FXB;P`$("\`<
  459. M"$[N_=A,[P,```0L;()N3N[^DB)O``0L;()N3N[^F$[Z``(B;P`$+&R";D[N!
  460. M_H9,[P`#``0L;()N3N[^SD[Z``(@;P`$+&R";D[N_H!,[P,```0L;(*03N[_<
  461. MH"!O``0L;(*03N[_IB!O``0L;(*03N[_L@```^P````!`````0``!'``````-
  462. M```#\@```^H```"8&UM#``@``````$[Y`````#`Q,C,T-38W.#EA8F-D968`D
  463. M```@("`@("`@("`P,#`P,"`@("`@("`@("`@("`@("`@()!`0$!`0$!`0$!`@
  464. M0$!`0$`,#`P,#`P,#`P,0$!`0$!`0`D)"0D)"0$!`0$!`0$!`0$!`0$!`0$!_
  465. M`0$!0$!`0$!`"@H*"@H*`@("`@("`@("`@("`@("`@("`@)`0$!`(```````'
  466. M```````````!``````$``````````````````````0$````!````````````%
  467. M``````````$"`````0``````````````````````````````````````````$
  468. M`````````````````````````````````````````````````````````````
  469. M`````````````````````````````````````````````````````````````
  470. M`````````````````````````````````````````````````````````````
  471. M`````````````````````````````````````````````````````````````
  472. M`````````````````````````````````````````````````````````````
  473. M`````````````````````````````````````````````````````````````
  474. M`````````````````````````````````````````````````````````````
  475. M```````````````````````````````````````````4``````/R```#ZP``W
  476. &``$```/RV
  477. ``
  478. end
  479. size 6396
  480. SHAR_EOF
  481. #    End of shell archive
  482. exit 0
  483. -- 
  484. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  485. Have five nice days.
  486.